package com.leaf.u.basehttp.http;

import com.leaf.u.basehttp.http.request.AsyncCall;
import com.leaf.u.basehttp.http.request.LeafCallQueue;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by bz on 2017/6/12.
 */

public class Dispatcher {

    private int maxRequests = 64;//

    private int maxRequestsPerHost = 5;

    private ExecutorService executorService;

    private final Map<String, LeafCallQueue> queues = new ConcurrentHashMap<>();
    /**
     * Ready async calls in the order they'll be run.
     */
    private final Map<String, AsyncCall> readyAsyncCalls = new ConcurrentHashMap<>();

    /**
     * Running asynchronous calls. Includes canceled calls that haven't finished yet.
     */
    private final Map<String, AsyncCall> runningAsyncCalls = new ConcurrentHashMap<>();

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>());
        }
        return executorService;
    }

    public synchronized void enqueue(LeafCallQueue queue) {

//        if (queues.containsKey(queue.uid())) {
//            //TODO 回调onfaild接口
//            return;
//        }
        queues.put(queue.uid(), queue);
        List<AsyncCall> callQueue = queue.getCallQueue();
        for (AsyncCall call : callQueue) {
            enqueue(call);
        }
    }

    public synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests) {
            runningAsyncCalls.put(call.uid(), call);
            executorService().execute(call);
        } else {
            readyAsyncCalls.put(call.uid(), call);
        }
    }

    private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

        Set<String> keys = readyAsyncCalls.keySet();
        for (String key : keys) {
            AsyncCall asyncCall = readyAsyncCalls.remove(key);
            runningAsyncCalls.put(asyncCall.uid(), asyncCall);
            executorService().execute(asyncCall);
            if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
    }


    public synchronized void finished(LeafCallQueue queue) {
        //队列完成的时候将任务移除
        queues.remove(queue.uid());
    }

    /**
     * Used by {@code AsyncCall#run} to signal completion.
     */
    public synchronized void finished(AsyncCall call) {
        finished(runningAsyncCalls, call);
        Set<String> keys = queues.keySet();
        for (String key : keys) {
            LeafCallQueue callQueue = queues.get(key);
            callQueue.finish(call);
        }
    }

    private void finished(Map calls, AsyncCall call) {
        synchronized (this) {
            calls.remove(call.uid());
            promoteCalls();
        }
    }
}
